package com.minidb.storage;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Collectors;
import java.util.Comparator;

/**
 * FileManager
 * - 管理表目录与页文件
 * - NEW: 为每个表维护持久化的空闲页清单 free_pages.meta
 * - NEW: 提供按实际存在页ID枚举 listPageIds()
 * - NEW: allocatePage 优先复用 free list，否则分配 maxId+1
 */
public class FileManager {
    private final Path root;

    public FileManager(String rootDir) {
        this.root = Paths.get(rootDir);
        try { Files.createDirectories(root); } catch (IOException ignored) {}
    }

    private Path tableDir(String table) { return root.resolve(table); }
    private Path pagePath(String table, int id) { return tableDir(table).resolve("page_" + id + ".bin"); }
    private Path freeListPath(String table) { return tableDir(table).resolve("free_pages.meta"); }

    public boolean createTableFile(String table) {
        try {
            Path dir = tableDir(table);
            if (Files.exists(dir)) return false;
            Files.createDirectories(dir);
            // 初始化空的 free list
            saveFreeList(table, new ArrayList<>());
            return true;
        } catch (IOException e) {
            throw new RuntimeException("创建表失败: " + e.getMessage());
        }
    }

    /** NEW: 返回该表目录下实际存在的页ID，升序 */
    public List<Integer> listPageIds(String table) {
        Path dir = tableDir(table);
        if (!Files.exists(dir)) return Collections.emptyList();
        try (var stream = Files.list(dir)) {
            return stream
                    .filter(f -> f.getFileName().toString().startsWith("page_") && f.getFileName().toString().endsWith(".bin"))
                    .map(p -> {
                        String name = p.getFileName().toString(); // page_X.bin
                        String mid = name.substring(5, name.length() - 4);
                        try { return Integer.parseInt(mid); } catch (NumberFormatException e) { return null; }
                    })
                    .filter(Objects::nonNull)
                    .sorted()
                    .collect(Collectors.toList());
        } catch (IOException e) {
            return Collections.emptyList();
        }
    }

    /** NEW: 返回最大页ID（无页返回 -1） */
    public int getMaxPageId(String table) {
        List<Integer> ids = listPageIds(table);
        return ids.isEmpty() ? -1 : ids.get(ids.size() - 1);
    }

    /** CHANGED/NEW: pageCount -> 返回实际页文件数（注意：若有空洞，则不等于 maxId+1） */
    public int getPageCount(String table) {
        return listPageIds(table).size();
    }

    /** NEW: 分配页：优先复用 free list，否则分配 maxId+1 */
    public synchronized Page allocatePage(String table) {
        try { Files.createDirectories(tableDir(table)); } catch (IOException ignored) {}
        List<Integer> free = loadFreeList(table);
        int id;
        if (!free.isEmpty()) {
            // LIFO 复用
            id = free.remove(free.size() - 1);
            saveFreeList(table, free);
        } else {
            id = getMaxPageId(table) + 1;
        }
        Page page = new Page(id, table);
        writePage(table, page);
        return page;
    }

    public Page readPage(String table, int id) {
        try {
            byte[] bytes = Files.readAllBytes(pagePath(table, id));
            return Page.fromBytes(bytes, table);
        } catch (IOException e) {
            throw new RuntimeException("读页失败: " + e.getMessage());
        }
    }

    public void writePage(String table, Page page) {
        try {
            Files.createDirectories(tableDir(table));
            Files.write(pagePath(table, page.getId()), page.toBytes(),
                    StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
        } catch (IOException e) {
            throw new RuntimeException("写页失败: " + e.getMessage());
        }
    }

    /** CHANGED: freePage 现在把页文件删除（如存在）并加入 free list 持久化 */
    public synchronized void freePage(String table, int id) {
        try {
            Files.deleteIfExists(pagePath(table, id));
        } catch (IOException ignored) {}
        List<Integer> free = loadFreeList(table);
        if (!free.contains(id)) {
            free.add(id);
            Collections.sort(free);
            saveFreeList(table, free);
        }
    }

    // --------------- 兼容别名（方便和检查表中的调用名对齐） ----------------
    public Page read_page(String table, int id) { return readPage(table, id); }
    public void write_page(String table, Page page) { writePage(table, page); }
    public Page allocate_page(String table) { return allocatePage(table); }

    /**
     * 删除表的所有文件和目录
     * @param table 表名
     * @return 删除成功返回true，否则返回false
     */
    public boolean deleteTableFile(String table) {
        try {
            Path dir = tableDir(table);
            if (!Files.exists(dir)) {
                return true; // 目录不存在，认为删除成功
            }
            
            // 递归删除目录及其所有内容
            Files.walk(dir)
                    .sorted(Comparator.reverseOrder())
                    .map(Path::toFile)
                    .forEach(file -> {
                        if (!file.delete()) {
                            System.err.println("无法删除文件: " + file.getAbsolutePath());
                        }
                    });
            
            return !Files.exists(dir);
        } catch (IOException e) {
            System.err.println("删除表文件失败: " + e.getMessage());
            return false;
        }
    }

    // ---------- free list 持久化实现（每行一个 int） ----------
    private List<Integer> loadFreeList(String table) {
        Path p = freeListPath(table);
        if (!Files.exists(p)) return new ArrayList<>();
        try {
            List<String> lines = Files.readAllLines(p, StandardCharsets.UTF_8);
            List<Integer> out = new ArrayList<>();
            for (String s : lines) {
                s = s.trim();
                if (s.isEmpty()) continue;
                try { out.add(Integer.parseInt(s)); } catch (NumberFormatException ignored) {}
            }
            Collections.sort(out);
            return out;
        } catch (IOException e) {
            return new ArrayList<>();
        }
    }

    private void saveFreeList(String table, List<Integer> ids) {
        Path p = freeListPath(table);
        try {
            Files.createDirectories(tableDir(table));
            String content = ids.stream().map(String::valueOf).collect(Collectors.joining("\n"));
            Files.writeString(p, content, StandardCharsets.UTF_8,
                    StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
        } catch (IOException ignored) {}
    }
}
